/*
 * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <sysupgrade.h>

#define SBL_VERSION_FILE       "sbl_version"
#define TZ_VERSION_FILE        "tz_version"
#define HLOS_VERSION_FILE      "hlos_version"
#define APPSBL_VERSION_FILE    "appsbl_version"
#define RPM_VERSION_FILE       "rpm_version"
#define DEVCFG_VERSION_FILE	"devcfg_version"
#define APDP_VERSION_FILE	"apdp_version"
#define VERSION_FILE_BASENAME  "/sys/devices/system/qfprom/qfprom0/"
#define AUTHENTICATE_FILE	"/sys/devices/system/qfprom/qfprom0/authenticate"
#define SEC_AUTHENTICATE_FILE  "/sys/sec_upgrade/sec_auth"
#define TEMP_KERNEL_PATH	"/tmp/tmp_kernel.bin"
#define TEMP_ROOTFS_PATH	"/tmp/rootfs_tmp.bin"
#define TEMP_METADATA_PATH	"/tmp/metadata.bin"
#define TEMP_SHA_KEY_PATH	"/tmp/sha_keyXXXXXX"
#define ROOTFS_OFFSET		65536
#define MAX_SBL_VERSION	11
#define MAX_HLOS_VERSION	32
#define MAX_TZ_VERSION		14
#define MAX_APPSBL_VERSION	14
#define MAX_RPM_VERSION	8
#define MAX_DEVCFG_VERSION	11
#define MAX_APDP_VERSION	8
#define HASH_P_FLAG		0x02200000
#define TMP_FILE_DIR		"/tmp/"
#define CERT_SIZE		2048
#define PRESENT		1
#define MBN_HDR_SIZE		40
#define SBL_HDR_SIZE		80
#define SIG_SIZE		256
#define NOT_PRESENT		0
#define SIG_CERT_2_SIZE	4352
#define SIG_CERT_3_SIZE	6400
#define SBL_NAND_PREAMBLE	10240
#define SBL_HDR_RESERVED	12
#define UBI_EC_HDR_MAGIC  0x55424923
#define UBI_VID_HDR_MAGIC 0x55424921
#define NO_OF_PROGRAM_HDRS   3

struct image_section sections[] = {
	{
		.section_type		= UBOOT_TYPE,
		.type			= "u-boot",
		.max_version		= MAX_APPSBL_VERSION,
		.file			= TMP_FILE_DIR,
		.version_file		= APPSBL_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x9"
	},
	{
		.section_type		= HLOS_TYPE,
		.type			= "hlos",
		.max_version		= MAX_HLOS_VERSION,
		.tmp_file		= TMP_FILE_DIR,
		.pre_op			= parse_elf_image_phdr,
		.file			= TMP_FILE_DIR,
		.version_file		= HLOS_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x17"
	},
	{
		.section_type		= HLOS_TYPE,
		.type			= "rootfs",
		.max_version		= MAX_HLOS_VERSION,
		.tmp_file		= TMP_FILE_DIR,
		.pre_op			= compute_sha_hash,
		.file			= TMP_FILE_DIR,
		.version_file		= HLOS_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x17"
	},
	{
		.section_type		= HLOS_TYPE,
		.type			= "ubi",
		.tmp_file		= TMP_FILE_DIR,
		.pre_op			= extract_binary,
		.max_version		= MAX_HLOS_VERSION,
		.file			= TEMP_KERNEL_PATH,
		.version_file		= HLOS_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x17"
	},
	{
		.section_type		= TZ_TYPE,
		.type			= "tz",
		.max_version		= MAX_TZ_VERSION,
		.file			= TMP_FILE_DIR,
		.version_file		= TZ_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x7"
	},
	{
		.section_type		= SBL_TYPE,
		.type			= "sbl1",
		.max_version		= MAX_SBL_VERSION,
		.file			= TMP_FILE_DIR,
		.version_file		= SBL_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x0"
	},
	{
		.section_type		= SBL_TYPE,
		.type			= "sbl2",
		.max_version		= MAX_SBL_VERSION,
		.file			= TMP_FILE_DIR,
		.version_file		= SBL_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x5"
	},
	{
		.section_type		= SBL_TYPE,
		.type			= "sbl3",
		.max_version		= MAX_SBL_VERSION,
		.file			= TMP_FILE_DIR,
		.version_file		= SBL_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x6"
	},
	{
		.section_type		= RPM_TYPE,
		.type			= "rpm",
		.max_version		= MAX_RPM_VERSION,
		.file			= TMP_FILE_DIR,
		.version_file		= RPM_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0xA"
	},
	{
		.section_type		= DEVCFG_TYPE,
		.type			= "devcfg",
		.max_version		= MAX_DEVCFG_VERSION,
		.file			= TMP_FILE_DIR,
		.version_file		= DEVCFG_VERSION_FILE,
		.is_present		= NOT_PRESENT,
		.img_code		= "0x5"
	},
	{
		.section_type           = APDP_TYPE,
		.type                   = "apdp",
		.max_version            = MAX_APDP_VERSION,
		.file                   = TMP_FILE_DIR,
		.version_file           = APDP_VERSION_FILE,
		.is_present             = NOT_PRESENT,
		.img_code               = "0x200"
	},
};

#define NO_OF_SECTIONS	ARRAY_SIZE(sections)
int src_size;

int check_mbn_elf(struct image_section **sec)
{
	int fd = open((*sec)->file, O_RDONLY);
	struct stat sb;
	uint8_t *fp;
	Elf32_Ehdr *elf;

	if (fd < 0) {
		perror((*sec)->file);
		return 0;
	}

	memset(&sb, 0, sizeof(struct stat));
	if (fstat(fd, &sb) == -1) {
		perror("fstat");
		close(fd);
		return 0;
	}

	fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (fp == MAP_FAILED) {
		perror("mmap");
		close(fd);
		return 0;
	}

	elf = (Elf32_Ehdr *)fp;
	if (!strncmp((char *)&(elf->e_ident[1]), "ELF", 3)) {
		/* EI_CLASS Check for 32/64-bit */
		if( ((int)(elf->e_ident[4])) == 2) {
			(*sec)->get_sw_id = get_sw_id_from_component_bin_elf64;
			(*sec)->split_components = split_code_signature_cert_from_component_bin_elf64;
		} else {
			(*sec)->get_sw_id = get_sw_id_from_component_bin_elf;
			(*sec)->split_components = split_code_signature_cert_from_component_bin_elf;
		}
	} else if (!strncmp((char *)&(((Elf32_Ehdr *)(fp + SBL_NAND_PREAMBLE))->e_ident[1]), "ELF", 3)) {
		if( ((int)(elf->e_ident[4])) == 2) {
			(*sec)->get_sw_id = get_sw_id_from_component_bin_elf64;
			(*sec)->split_components = split_code_signature_cert_from_component_bin_elf64;
		} else {
			(*sec)->get_sw_id = get_sw_id_from_component_bin_elf;
			(*sec)->split_components = split_code_signature_cert_from_component_bin_elf;
		}
	} else {
		(*sec)->get_sw_id = get_sw_id_from_component_bin;
		(*sec)->split_components = split_code_signature_cert_from_component_bin;
	}

	return 1;
}

int get_sections(void)
{
	DIR *dir = opendir(TMP_FILE_DIR);
	struct dirent *file;
	int i,data_size;
	struct image_section *sec;

	if (dir == NULL) {
		printf("Error accessing the image directory\n");
		return 0;
	}

	data_size = find_mtd_part_size("kernel");
	while ((file = readdir(dir)) != NULL) {
		for (i = 0, sec = &sections[0]; i < NO_OF_SECTIONS; i++, sec++) {
			/* Skip loading of ubi section if board is not from nand boot */
			if (data_size == -1 && !strncmp(sec->type, "ubi", strlen("ubi")))
				continue;
			if (!strncmp(file->d_name, sec->type, strlen(sec->type))) {
				strlcat(sec->file, file->d_name, sizeof(sec->file));
				if (sec->pre_op) {
					strlcat(sec->tmp_file, file->d_name,
							sizeof(sec->tmp_file));
					if (!sec->pre_op(sec)) {
						printf("Error extracting kernel from ubi\n");
						return 0;
					}
				}
				if (!check_mbn_elf(&sec)) {
					closedir(dir);
					return 0;
				}
				if (!sec->get_sw_id(sec)) {
					closedir(dir);
					return 0;
				}
				get_local_image_version(sec);
				sec->is_present = PRESENT;
				break;
			}
		}
	}
	closedir(dir);
	return 1;
}

int load_sections(void)
{
	DIR *dir;
	int i,data_size;
	struct dirent *file;
	struct image_section *sec;

	dir = opendir(TMP_FILE_DIR);
	if (dir == NULL) {
		printf("Error accessing the %s image directory\n", TMP_FILE_DIR);
		return 0;
	}

	data_size = find_mtd_part_size("kernel");
	while ((file = readdir(dir)) != NULL) {
		for (i = 0, sec = &sections[0]; i < NO_OF_SECTIONS; i++, sec++) {
			/* Skip loading of ubi section if board is not from nand boot */
			if (data_size == -1 && !strncmp(sec->type, "ubi", strlen("ubi")))
				continue;
			if (!strncmp(file->d_name, sec->type, strlen(sec->type))) {
				strlcat(sec->file, file->d_name, sizeof(sec->file));
				if (sec->pre_op) {
					strlcat(sec->tmp_file, file->d_name,
							sizeof(sec->tmp_file));
					if (!sec->pre_op(sec)) {
						printf("Error extracting %s from ubi\n",
									   sec->tmp_file);
						closedir(dir);
						return 0;
					}
				}
				sec->is_present = PRESENT;
				break;
			}
		}
	}
	closedir(dir);
	return 1;
}

/**
 * is_authentication_check_enabled() - checks whether installed image is
 * secure(1) or not(0)
 *
 */
int is_authentication_check_enabled(void)
{
	int fd = open(AUTHENTICATE_FILE, O_RDONLY);
	char authenticate_string[4];
	int len;

	if (fd == -1) {
		perror(AUTHENTICATE_FILE);
		return 0;
	}

	len = read(fd, authenticate_string, 1);
	close(fd);

	if (len > 0 && authenticate_string[0] == '0') {
		return 0;
	}

	return 1;
}

int is_tz_authentication_enabled(void)
{
	struct stat sb;

	if (stat(SEC_AUTHENTICATE_FILE, &sb) == -1) {
		perror("stat");
		return 0;
	}
	return 1;
}

int is_rootfs_auth_enabled(void)
{
	char command[36];
	int retval;

	snprintf(command, sizeof(command),"fw_printenv | grep -q rootfs_auth");

	retval = system(command);
	if (retval != 0) {
		return 0;
	}

	return 1;
}

/**
 * get_local_image_version() check the version file & if it exists, read the
 * value & save it into global variable local_version
 *
 */
int get_local_image_version(struct image_section *section)
{
	int len, fd;
	char local_version_string[16], version_file[64];
	struct stat st;

	snprintf(version_file, sizeof(version_file), "%s%s", VERSION_FILE_BASENAME, section->version_file);
	fd = open(version_file, O_RDONLY);
	if (fd == -1) {
		perror(version_file);
		return 0;
	}

	memset(&st, 0, sizeof(struct stat));
	fstat(fd, &st);

	len = st.st_size < sizeof(local_version_string) - 1 ? st.st_size :
							sizeof(local_version_string) - 1;
	if (read(fd, local_version_string, len) == -1) {
		close(fd);
		return 0;
	}
	local_version_string[len] = '\0';
	close(fd);

	section->local_version = atoi(local_version_string);
	printf("Local image version:%s\n", local_version_string);

	return 1;
}

/**
 * set_local_image_version() update the version of the image by writing the version
 * to the version file
 *
 */
int set_local_image_version(struct image_section *section)
{
	int fd;
	char version_string[16], version_file[64];
	int len;

	snprintf(version_file, sizeof(version_file), "%s%s", TMP_FILE_DIR, section->version_file);
	fd = open(version_file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
	if (fd == -1) {
		perror(version_file);
		return 0;
	}

	len = snprintf(version_string, 8, "%d", section->img_version);
	if (len < 0) {
		printf("Error in formatting the version string");
		return 0;
	}

	printf("Version to be updated:%s\n", version_string);
	if (write(fd, version_string, len) == -1) {
		printf("Error writing version to %s\n", version_file);
		close(fd);
		return 0;
	}

	close(fd);
	return 1;
}

/**
 * is_version_check_enabled() checks whether version check is
 * enabled(non-zero value) or not
 *
 */
int is_version_check_enabled()
{
	if (get_local_image_version(&sections[0]) != -1) {
		printf("Returning 1 from is_version_check_enabled because local_version_string is non-ZERO\n");
		return 1;
	}

	return 0;
}

char *find_value(char *buffer, char *search, int size)
{
	char *value = malloc(size * sizeof(char));
	int i, j;

	if (value == NULL) {
		return NULL;
	}

	for (i = 0; i < CERT_SIZE; i++) {
		for (j = 0; search[j] && (buffer[i + j] == search[j]); j++);
		if (search[j] == '\0') {
			strlcpy(value, &buffer[i - size], size);
			value[size - 1] = '\0';
			return value;
		}
	}
	free(value);
	return NULL;
}

/**
 * check_nand_preamble() compares first 12 bytes of section with
 * pre defined PREAMBLE value and returns 0 if both value matches
 */
int check_nand_preamble(uint8_t *mfp)
{
	char magic[12] = { 0xd1, 0xdc, 0x4b, 0x84,
			   0x34, 0x10, 0xd7, 0x73,
			   0x5a, 0x43, 0x0b, 0x7d };
	return memcmp(magic, mfp, sizeof(magic));
}

/**
 * get_sw_id_from_component_bin() parses the MBN header & checks image size v/s
 * code size. If both differ, it means signature & certificates are
 * appended at end.
 * Extract the attestation certificate & read the Subject & retreive the SW_ID.
 *
 * @bin_file: struct image_section *
 */
int get_sw_id_from_component_bin(struct image_section *section)
{
	Mbn_Hdr *mbn_hdr;
	int fd = open(section->file, O_RDONLY);
	struct stat sb;
	uint8_t *fp;
	int cert_offset;
	char *sw_version;
	int sig_cert_size;

	if (fd == -1) {
		perror(section->file);
		return 0;
	}

	memset(&sb, 0, sizeof(struct stat));
	if (fstat(fd, &sb) == -1) {
		perror("fstat");
		close(fd);
		return 0;
	}

	fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (fp == MAP_FAILED) {
		perror("mmap");
		close(fd);
		return 0;
	}

	mbn_hdr = (Mbn_Hdr *)fp;
	if (strstr(section->file, sections[4].type)) {
		uint32_t preamble = !check_nand_preamble(fp) ? SBL_NAND_PREAMBLE : 0;
		Sbl_Hdr *sbl_hdr = (Sbl_Hdr *)(fp + preamble);

		sig_cert_size = sbl_hdr->image_size - sbl_hdr->code_size;
		cert_offset = preamble + sbl_hdr->cert_ptr - sbl_hdr->image_dest_ptr +
				SBL_HDR_SIZE;
	} else {
		sig_cert_size = mbn_hdr->image_size - mbn_hdr->code_size;
		cert_offset = mbn_hdr->cert_ptr - mbn_hdr->image_dest_ptr + 40;
	}
	if (sig_cert_size != SIG_CERT_2_SIZE && sig_cert_size != SIG_CERT_3_SIZE) {
		printf("WARNING: signature certificate size is different\n");
		// ipq807x has certificate size as dynamic, hence ignore this check
	}

	printf("Image with version information\n");
	sw_version = find_value((char *)(fp + cert_offset), "SW_ID", 17);
	if (sw_version != NULL) {
		sw_version[8] = '\0';
		sscanf(sw_version, "%x", &section->img_version);
		printf("SW ID:%d\n", section->img_version);
		free(sw_version);
	}

	close(fd);
	return 1;
}

int process_elf(char *bin_file, uint8_t **fp, Elf32_Ehdr **elf, Elf32_Phdr **phdr, Mbn_Hdr **mbn_hdr)
{
	int fd = open(bin_file, O_RDONLY);
	struct stat sb;
	int version = 0;
	int i = 0;

	if (fd < 0) {
		perror(bin_file);
		return 0;
	}

	memset(&sb, 0, sizeof(struct stat));
	if (fstat(fd, &sb) == -1) {
		perror("fstat");
		close(fd);
		return 0;
	}

	*fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (*fp == MAP_FAILED) {
		perror("mmap");
		close(fd);
		return 0;
	}

	*elf = (Elf32_Ehdr *)*fp;
	while (strncmp((char *)&((*elf)->e_ident[1]), "ELF", 3)) {
		*fp = (uint8_t *)((char *)(*fp) + SBL_NAND_PREAMBLE);
		*elf = (Elf32_Ehdr *)*fp;
	}

	*phdr = (Elf32_Phdr *)(*fp + (*elf)->e_phoff);
	for (i = 0; i < (*elf)->e_phnum; i++, (*phdr)++) {
		if ((*phdr)->p_flags == HASH_P_FLAG) {
			*mbn_hdr = (Mbn_Hdr *)(*fp + (*phdr)->p_offset);
			if ((*mbn_hdr)->image_size != (*mbn_hdr)->code_size) {
				version = 1;
				break;
			} else {
				printf("Error: Image without version information\n");
				close(fd);
				return 0;
			}
		}
	}

	if (version != 1) {
		printf("Error: Image without version information\n");
		return 0;
	}

	close(fd);
	return 1;
}

int process_elf64(char *bin_file, uint8_t **fp, Elf64_Ehdr **elf, Elf64_Phdr **phdr, Mbn_Hdr **mbn_hdr)
{
	struct stat sb;
	int i, fd, version = 0;

	fd = open(bin_file, O_RDONLY);
	if (fd < 0) {
		perror(bin_file);
		return 0;
	}

	memset(&sb, 0, sizeof(struct stat));
	if (fstat(fd, &sb) == -1) {
		perror("fstat");
		close(fd);
		return 0;
	}

	*fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (*fp == MAP_FAILED) {
		perror("mmap");
		close(fd);
		return 0;
	}

	*elf = (Elf64_Ehdr *)*fp;
	while (strncmp((char *)&((*elf)->e_ident[1]), "ELF", 3)) {
		*fp = (uint8_t *)((char *)(*fp) + SBL_NAND_PREAMBLE);
		*elf = (Elf64_Ehdr *)*fp;
	}

	*phdr = (Elf64_Phdr *)(*fp + (*elf)->e_phoff);
	for (i = 0; i < (*elf)->e_phnum; i++, (*phdr)++) {
		if ((*phdr)->p_flags == HASH_P_FLAG) {
			*mbn_hdr = (Mbn_Hdr *)(*fp + (*phdr)->p_offset);
			if ((*mbn_hdr)->image_size != (*mbn_hdr)->code_size) {
				version = 1;
				break;
			} else {
				printf("Error: Image without version information\n");
				close(fd);
				return 0;
			}
		}
	}

	if (version != 1) {
		printf("Error: Image without version information\n");
		return 0;
	}

	close(fd);
	return 1;
}

/**
 * get_sw_id_from_component_bin_elf() parses the ELF header to get the MBN header
 * of the hash table segment. Parses the MBN header of hash table segment & checks
 * total size v/s actual component size. If both differ, it means signature &
 * certificates are appended at end.
 * Extract the attestation certificate & read the Subject & retreive the SW_ID.
 *
 * @bin_file: struct image_section *
 */
int get_sw_id_from_component_bin_elf(struct image_section *section)
{
	Elf32_Ehdr *elf;
	Elf32_Phdr *phdr;
	Mbn_Hdr *mbn_hdr;
	uint8_t *fp;
	int cert_offset;
	char *sw_version;

	if (!process_elf(section->file, &fp, &elf, &phdr, &mbn_hdr)) {
		return 0;
	}

	cert_offset = mbn_hdr->cert_ptr - mbn_hdr->image_dest_ptr + 40;
	printf("Image with version information\n");
	sw_version = find_value((char *)(fp + phdr->p_offset + cert_offset), "SW_ID", 17);
	if (sw_version) {
		sw_version[8] = '\0';
		sscanf(sw_version, "%x", &section->img_version);
		printf("SW ID:%d\n", section->img_version);
		free(sw_version);
	}

	return 1;
}

/**
 * get_sw_id_from_component_bin_elf64() parses the ELF64 header to get the MBN header
 * of the hash table segment. Parses the MBN header of hash table segment & checks
 * total size v/s actual component size. If both differ, it means signature &
 * certificates are appended at end.
 * Extract the attestation certificate & read the Subject & retreive the SW_ID.
 *
 32_Phdr *phdr;* @bin_file: struct image_section *
 */
int get_sw_id_from_component_bin_elf64(struct image_section *section)
{
	Elf64_Ehdr *elf;
	Elf64_Phdr *phdr;
	Mbn_Hdr *mbn_hdr;
	uint8_t *fp;
	int cert_offset;
	char *sw_version;

	if (!process_elf64(section->file, &fp, &elf, &phdr, &mbn_hdr)) {
		return 0;
	}

	cert_offset = mbn_hdr->code_size + mbn_hdr->sig_sz + 40;
	printf("Image with version information64\n");
	sw_version = find_value((char *)(fp + phdr->p_offset + cert_offset), "SW_ID", 17);
	if (sw_version) {
		sw_version[8] = '\0';
		sscanf(sw_version, "%x", &section->img_version);
		printf("SW ID:%d\n", section->img_version);
		free(sw_version);
	}

	return 1;
}

int find_mtd_part_size(char *mtdname)
{
	char prefix[] = "/dev/mtd";
	char dev[PATH_MAX];
	int i = -1, fd;
	int vol_size;
	int flag = 0;
	char mtd_part[256];
	FILE *fp = fopen("/proc/mtd", "r");
	mtd_info_t mtd_dev_info;

	if (fp == NULL) {
		printf("Error finding mtd part\n");
		return -1;
	}

	while (fgets(dev, sizeof(dev), fp)) {
		if (strstr(dev, mtdname)) {
			flag = 1;
			break;
		}
		i++;
	}
	fclose(fp);

	if (flag != 1) {
		printf("%s block not found\n", mtdname);
		return -1;
	}

	snprintf(mtd_part, sizeof(mtd_part), "%s%d", prefix, i);

	fd = open(mtd_part, O_RDWR);
	if (fd == -1) {
		return -1;
	}

	if (ioctl(fd, MEMGETINFO, &mtd_dev_info) == -1) {
		printf("Error getting block size\n");
		close(fd);
		return -1;
	}

	vol_size = mtd_dev_info.erasesize;
	close(fd);

	return vol_size;
}

/**
 * Helper function to dynamically get volume id
 */
int get_ubi_volume_id(char *vol_name)
{
	int i, number_of_ubi_volumes;
	char ubi_vol_count[] = "/sys/class/ubi/ubi0/volumes_count";
	char prefix[] = "/sys/class/ubi/ubi0_";
	char suffix[] = "/name";
	char ubi_vol[256];
	char ubi_vol_name[256];
	char current_ubi_volumes_count[256];
	FILE *fp;

	fp = fopen(ubi_vol_count, "r");
	fgets(current_ubi_volumes_count, sizeof(current_ubi_volumes_count), fp);
	number_of_ubi_volumes = atoi(current_ubi_volumes_count);
	printf("number of ubi volumes = %d\n", number_of_ubi_volumes);

	for (i = 0; i < number_of_ubi_volumes; i++)
	{
		snprintf(ubi_vol, sizeof(ubi_vol), "%s%d%s", prefix, i, suffix);
		fp = fopen(ubi_vol, "r");
		fgets(ubi_vol_name, sizeof(ubi_vol_name), fp);
		if (strstr(ubi_vol_name, vol_name)) {
			printf("%s volume id = %d\n", vol_name, i);
			fclose(fp);
			return i;
		}
	}

	return -1;
}

/**
 * For NAND image, kernel and rootfs image is ubinized.
 * Hence need to un-ubinize the ubi image and extract both kernel
 * and rootfs images. Kernel image section and volume name are passed as
 * argument in extract_kernel_binary(). After kernel extraction,
 * parse_elf_image_phdr() is called which parses the ELF32 program header
 * to get the ELF header of rootfs metadata.
 */

int extract_binary(struct image_section *section)
{
	extract_kernel_binary(section, "kernel");
	parse_elf_image_phdr(section);
	return 1;
}

/**
 * In case of NAND image, Kernel image is ubinized & version information is
 * part of Kernel image. Hence need to un-ubinize the image.
 * To get the kernel image, Find the volume with kernel's volume id. Kernel image
 * is fragmented and hence we need to assemble it to get complete image.
 * In UBI image, first look for UBI#, which is the magic number used to identify
 * each eraseble block. Parse the UBI header, which starts with UBI# & get
 * the VID(volume ID) header offset as well as Data offset.
 * Traverse to VID header offset & check the volume ID. If it is matching with
 * kernel Volume id extracted based on ubi sysfs, Kernel image is stored in
 * this volume. Use Data offset to extract the Kernel image.
 *
 * @bin_file: struct image_section *
 */

int extract_kernel_binary(struct image_section *section, char *volname)
{
	char *ifname, *ofname;

	strlcpy(section->file, TEMP_KERNEL_PATH, sizeof(TEMP_KERNEL_PATH));
	ifname = section->tmp_file;
	ofname = section->file;

	if (extract_ubi_volume(volname, ifname, ofname) != 1) {
		printf("Image extraction failed\n");
		return 0;
	}
	return 1;
}

/**
 * extract_ubi_volume() extracts both kernel image and rootfs image
 * from UBI image. extract_kernel_binary() passes volume name,
 * UBI image as Input file name, kernel or rootfs as Output file name.
 * This function finds respective UBI volume ID, identifies each eraseble
 * block. Parses UBI header and gets Volume ID header offset as well as
 * Data offset. When volume ID matches for kernel and rootfs, Uses data offset
 * to extract both images separately.
 */

int extract_ubi_volume(char *vol_name, char *if_name, char *of_name)
{
	struct ubi_ec_hdr *ubi_ec;
	struct ubi_vid_hdr *ubi_vol;
	uint8_t *fp;
	int fd, ofd, magic, data_size, vid_hdr_offset, data_offset;
	int ret_vol_id, curr_vol_id;
	struct stat sb;

	if (if_name == NULL) {
		return 0;
	}

	fd = open(if_name, O_RDONLY);
	if (fd < 0) {
		perror(if_name);
		return 0;
	}

	memset(&sb, 0, sizeof(struct stat));
	if (fstat(fd, &sb) == -1) {
		perror("fstat");
		close(fd);
		return 0;
	}

	fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (fp == MAP_FAILED) {
		perror("mmap");
		close(fd);
		return 0;
	}

	ofd = open(of_name, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
	if (ofd == -1) {
		perror(of_name);
		close(fd);
		return 0;
	}

	data_size = find_mtd_part_size(vol_name);
	if (data_size == -1) {
		printf("Error finding data size\n");
		return 0;
	}

	ret_vol_id = get_ubi_volume_id(vol_name);
	if (ret_vol_id == -1) {
		printf("Wrong ubi volume id for kernel\n");
		return 0;
	}

	ubi_ec = (struct ubi_ec_hdr *)fp;
	magic = be32_to_cpu(ubi_ec->magic);
	while (magic == UBI_EC_HDR_MAGIC) {
		vid_hdr_offset = be32_to_cpu(ubi_ec->vid_hdr_offset);
		data_offset = be32_to_cpu(ubi_ec->data_offset);
		ubi_vol = (struct ubi_vid_hdr *)((uint8_t *)ubi_ec + vid_hdr_offset);
		magic = be32_to_cpu(ubi_vol->magic);
		if (magic != UBI_VID_HDR_MAGIC) {
			printf("Wrong ubi format\n");
			close(ofd);
			close(fd);
			return 0;
		}
		curr_vol_id = be32_to_cpu(ubi_vol->vol_id);
		if (curr_vol_id == ret_vol_id) {
			if (write(ofd, (void *)((uint8_t *)ubi_ec + data_offset), data_size) == -1) {
				printf("Write error\n");
				close(fd);
				close(ofd);
				return 0;
			}
		}
		ubi_ec = (struct ubi_ec_hdr *)((uint8_t *)ubi_ec + data_offset + data_size);
		magic = be32_to_cpu(ubi_ec->magic);
	}
	if (munmap(fp, sb.st_size) == -1) {
		perror("munmap");
		close(ofd);
		close(fd);
		return 0;
	}
	close(ofd);
	close(fd);
	if (!strncmp(vol_name, "ubi_rootfs", strlen("ubi_rootfs"))) {
		extract_rootfs_binary(of_name);
	}
	printf("%s extracted from ubi image\n", vol_name);
	return 1;
}

/**
 * check_image_exist() used to check whether the ubi image for NAND
 * or rootfs image for EMMC are available in /tmp directory.
 * If respective image was found, the filename will be passed. If
 * not found, it returns NULL
 */

char * check_image_exist(char *imgname) {
	DIR* FD;
	struct dirent* in_file;
	char extension[256] = "/tmp/";

	/* Scanning the in directory */
	if (NULL == (FD = opendir ("/tmp")))
	{
		fprintf(stderr, "Error : Failed to open input directory\n");
		return NULL;
	}

	while ((in_file = readdir(FD)))
	{
		if (strstr(in_file->d_name, imgname)) {
			printf("%s file found\n", in_file->d_name);
			strlcat(extension, in_file->d_name, sizeof(extension));
			strlcpy(in_file->d_name, extension, sizeof(extension));
			return in_file->d_name;
		}
	}
	return NULL;
}

/**
 * In case of EMMC, extract_rootfs_binary() extracts the rootfs image
 * till Deadcode from existing rootfs image. After extraction, the new rootfs
 * image will replace old rootfs image. This function removes padded
 * binaries and helps to authenticate rootfs image using sec_auth
 */

int extract_rootfs_binary(char *filename)
{
	int ifd;
	uint8_t *fp;
	struct stat sb;

	if (filename == NULL) {
		return 0;
	}

	ifd = open(filename, O_RDWR);
	if (ifd < 0) {
		perror(filename);
		return 0;
	}

	memset(&sb, 0, sizeof(struct stat));
	if (fstat(ifd, &sb) == -1) {
		perror("fstat");
		close(ifd);
		return 0;
	}

	fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, ifd, 0);
	if (fp == MAP_FAILED) {
		perror("mmap");
		close(ifd);
		return 0;
	}

	int offset = 0,dead_off = sb.st_size;
	while ( offset <= sb.st_size)
	{
		if ((fp[offset] == 0xde) && (fp[offset+1] == 0xad) && (fp[offset+2] == 0xc0) && (fp[offset+3] == 0xde)) {
			dead_off=offset;
			break;
		}
		offset += ROOTFS_OFFSET;
	}

	if (munmap(fp, sb.st_size) == -1) {
		perror("munmap");
		close(ifd);
		return 0;
	}

	close(ifd);
	truncate(filename, dead_off);
	return 1;
}

/**
 * The digest functions output the message digest of a supplied file and
 * write sha-hash key to /tmp/sha_keyXXXXXX file
 */
int compute_sha_hash(struct image_section *section)
{
	char sha_hash[] = TEMP_SHA_KEY_PATH;
	char command[300];
	int retval;

#ifdef USE_SHA384
	retval = snprintf(command, sizeof(command),
		"openssl dgst -sha384 -binary -out %s %s", sha_hash, section->tmp_file);
#endif
#ifdef USE_SHA256
	retval = snprintf(command, sizeof(command),
		"openssl dgst -sha256 -binary -out %s %s", sha_hash, section->tmp_file);
#endif
	if (retval < 0) {
		return retval;
	}
	retval = system(command);
	if (retval != 0) {
		printf("Error generating sha-hash, command : %s\n",command);
		return 0;
	}

	printf("sha_hash file is created: %s \n",sha_hash);
	return 1;
}
/**
 * is_image_version_higher() iterates through each component and check
 * versions against locally installed version.
 * If newer component version is lower than locally insatlled image,
 * abort the FW upgrade process.
 *
 * @img: char *
 */
int is_image_version_higher(void)
{
	int i;
	for (i = 0; i < NO_OF_SECTIONS; i++) {
		if (!sections[i].is_present) {
			continue;
		}

		if (sections[i].local_version > sections[i].img_version) {
			printf("Version of image %s (%d) is lower than minimal supported version(%d)\n",
					sections[i].file,
					sections[i].img_version,
					sections[i].local_version);
			return 0;
		}

		if (sections[i].img_version > sections[i].max_version) {
			printf("Version of image %s (%d) is higher than maximum supported version(%d)\n",
					sections[i].file,
					sections[i].img_version,
					sections[i].max_version);
		}
	}

	return 1;
}

/**
 * Update the version information file based on currently SW_ID being installed.
 *
 */
int update_version(void)
{
	int i;
	for (i = 0; i < NO_OF_SECTIONS; i++) {
		if (!sections[i].is_present) {
			continue;
		}

		if (set_local_image_version(&sections[i]) != 1) {
			printf("Error updating version of %s\n", sections[i].file);
			return 0;
		}
	}

	return 1;
}

int check_image_version(void)
{
	if (is_version_check_enabled() == 0) {
		printf("Version check is not enabled, upgrade to continue !!!\n");
		return 1;
	}

	if (is_image_version_higher() == 0) {
		printf("New image versions are lower than existing image, upgrade to STOP !!!\n");
		return 0;
	}

	if (update_version() != 1) {
		printf("Error while updating verison information\n");
		return 0;
	}
	printf("Update completed!\n");

	return 1;
}

/**
 * split_code_signature_cert_from_component_bin splits the component
 * binary by splitting into code(including MBN header), signature file &
 * attenstation certificate.
 *
 * @bin_file: char *
 * @src: char *
 * @sig: char *
 * @cert: char *
 */
int split_code_signature_cert_from_component_bin(struct image_section *section,
		char **src, char **sig, char **cert)
{
	Mbn_Hdr *mbn_hdr;
	Sbl_Hdr *sbl_hdr;
	int fd = open(section->file, O_RDONLY);
	uint8_t *fp;
	int sig_offset = 0;
	int src_offset = 0;
	int cert_offset = 0;
	struct stat sb;
	int sig_cert_size;

	if (fd == -1) {
		perror(section->file);
		return 0;
	}

	memset(&sb, 0, sizeof(struct stat));
	if (fstat(fd, &sb) == -1) {
		perror("fstat");
		close(fd);
		return 0;
	}

	fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (fp == MAP_FAILED) {
		perror("mmap");
		close(fd);
		return 0;
	}

	mbn_hdr = (Mbn_Hdr *)fp;
	if (strstr(section->file, sections[4].type)) {
		uint32_t preamble = !check_nand_preamble(fp) ? SBL_NAND_PREAMBLE : 0;

		sbl_hdr = (Sbl_Hdr *)(fp + preamble);
		src_offset = preamble;
		sig_offset = preamble + sbl_hdr->sig_ptr - sbl_hdr->image_dest_ptr +
				SBL_HDR_SIZE;
		cert_offset = preamble + sbl_hdr->cert_ptr - sbl_hdr->image_dest_ptr +
				SBL_HDR_SIZE;
		sig_cert_size = sbl_hdr->image_size - sbl_hdr->code_size;
		src_size = sbl_hdr->sig_ptr - sbl_hdr->image_dest_ptr + SBL_HDR_SIZE;
	} else {
		sig_cert_size = mbn_hdr->image_size - mbn_hdr->code_size;
		src_size = mbn_hdr->sig_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
		sig_offset += mbn_hdr->sig_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
		cert_offset += mbn_hdr->cert_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
	}

	if (sig_cert_size != SIG_CERT_2_SIZE && sig_cert_size != SIG_CERT_3_SIZE) {
		printf("WARNING: signature certificate size is different\n");
	}
        *src = malloc(src_size + 1);
	if (*src == NULL) {
		close(fd);
		return 0;
	}
	memcpy(*src, fp + src_offset, src_size);
	(*src)[src_size] = '\0';

	*sig = malloc((SIG_SIZE + 1) * sizeof(char));
	if (*sig == NULL) {
		free(*src);
		return 0;
	}
	memcpy(*sig, fp + sig_offset, SIG_SIZE);
	(*sig)[SIG_SIZE] = '\0';

	*cert = malloc((CERT_SIZE + 1) * sizeof(char));
	if (*cert == NULL) {
		free(*src);
		free(*sig);
		return 0;
	}
	memcpy(*cert, fp + cert_offset, CERT_SIZE);
	(*cert)[CERT_SIZE] = '\0';

	close(fd);
	return 1;
}

/**
 * split_code_signature_cert_from_component_bin_elf splits the component
 * binary by splitting into code(including ELF header), signature file &
 * attenstation certificate.
 *
 * @bin_file: char *
 * @src: char *
 * @sig: char *
 * @cert: char *
 */
int split_code_signature_cert_from_component_bin_elf(struct image_section *section,
		char **src, char **sig, char **cert)
{
	Elf32_Ehdr *elf;
	Elf32_Phdr *phdr;
	Mbn_Hdr *mbn_hdr;
	uint8_t *fp;
	int sig_offset;
	int cert_offset;
	int len;

	if (!process_elf(section->file, &fp, &elf, &phdr, &mbn_hdr)) {
		return 0;
	}

	sig_offset = mbn_hdr->sig_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
	len = sig_offset;
	*src = malloc((len + 1) * sizeof(char));
	if (*src == NULL) {
		return 0;
	}
	memcpy(*src, fp + phdr->p_offset, len);
	src_size = len;
	(*src)[len] = '\0';

	*sig = malloc((SIG_SIZE + 1) * sizeof(char));
	if (*sig == NULL) {
		free(*src);
		return 0;
	}
	memcpy(*sig, fp + phdr->p_offset + sig_offset, SIG_SIZE);
	(*sig)[SIG_SIZE] = '\0';

	cert_offset = mbn_hdr->cert_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
	*cert = malloc((CERT_SIZE + 1) * sizeof(char));
	if (*cert == NULL) {
		free(*src);
		free(*sig);
		return 0;
	}
	memcpy(*cert, fp + phdr->p_offset + cert_offset, CERT_SIZE);
	(*cert)[CERT_SIZE] = '\0';

	return 1;
}

/**
 * split_code_signature_cert_from_component_bin_elf64 splits the component
 * binary by splitting into code(including ELF header), signature file &
 * attenstation certificate.
 *
 * @bin_file: char *
 * @src: char *
 * @sig: char *
 * @cert: char *
 */
int split_code_signature_cert_from_component_bin_elf64(struct image_section *section,
                char **src, char **sig, char **cert)
{
	Elf64_Ehdr *elf;
	Elf64_Phdr *phdr;
	Mbn_Hdr *mbn_hdr;
	uint8_t *fp;
	int len, sig_offset, cert_offset;

	if (!process_elf64(section->file, &fp, &elf, &phdr, &mbn_hdr)) {
		return 0;
	}

	sig_offset = mbn_hdr->code_size + MBN_HDR_SIZE;
	len = sig_offset;
	*src = malloc((len + 1) * sizeof(char));
	if (*src == NULL) {
		return 0;
	}

	memcpy(*src, fp + phdr->p_offset, len);
	src_size = len;
	(*src)[len] = '\0';

	*sig = malloc((SIG_SIZE + 1) * sizeof(char));
	if (*sig == NULL) {
		free(*src);
		return 0;
	}

	memcpy(*sig, fp + phdr->p_offset + sig_offset, SIG_SIZE);
	(*sig)[SIG_SIZE] = '\0';

	cert_offset = mbn_hdr->code_size + mbn_hdr->sig_sz + MBN_HDR_SIZE;
	*cert = malloc((CERT_SIZE + 1) * sizeof(char));
	if (*cert == NULL) {
		free(*src);
		free(*sig);
		return 0;
	}
	memcpy(*cert, fp + phdr->p_offset + cert_offset, CERT_SIZE);
	(*cert)[CERT_SIZE] = '\0';

	return 1;
}

/**
 * parse_elf_image_phdr() parses the ELF32 program header to get the
 * ELF header of rootfs metadata. It parses the metadata and writes
 * to a new /tmp/metadata.bin file.
 */
int parse_elf_image_phdr(struct image_section *section)
{
	Elf32_Ehdr *ehdr; /* Elf header structure pointer */
	Elf32_Phdr *phdr; /* Program header structure pointer */
        struct stat sb;
        uint8_t *fp;
	int i;

	if (!is_rootfs_auth_enabled()) {
		return 1;
	}

        int fd = open(section->file, O_RDONLY);

        if (fd < 0) {
                perror(section->file);
                return 0;
        }

        memset(&sb, 0, sizeof(struct stat));
        if (fstat(fd, &sb) == -1) {
                perror("fstat");
                close(fd);
                return 0;
        }

        fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (fp == MAP_FAILED) {
                perror("mmap");
                close(fd);
                return 0;
        }

        ehdr = (Elf32_Ehdr *)fp;
	phdr = (Elf32_Phdr *)(((char*)fp) + ehdr->e_phoff);
	printf(" ELF headers are initialized\n");

	if (ehdr->e_type != ET_EXEC) {
		printf("Not a valid elf image\n");
		close(fd);
		return 0;
	}

	/* Load each program header */
	for (i = 0; i < NO_OF_PROGRAM_HDRS; ++i) {
		if(phdr->p_type == PT_LOAD) {
			printf(" PT_LOAD Segment Found\n");
			printf("Parsing img_info load addr 0x%x offset 0x%x size 0x%x type 0x%x\n",
			phdr->p_paddr, phdr->p_offset, phdr->p_filesz, phdr->p_type);

			int size = sb.st_size - (phdr->p_offset + phdr->p_filesz );
			create_file(TEMP_METADATA_PATH, (char *)(fp + phdr->p_offset + phdr->p_filesz), size);
			printf("rootfs meta data file: %s created with size:%x\n",TEMP_METADATA_PATH, size);

			close(fd);
			return 1;
		}
		++phdr;
	}

	close(fd);
	return 1;
}

/**
 * being used to calculate the image hash
 *
 */
#define SW_MASK		0x3636363636363636ull

void generate_swid_ipad(char *sw_id, unsigned long long *swid_xor_ipad)
{
	unsigned long long int val;

	val = strtoull(sw_id, NULL, 16);
	*swid_xor_ipad = val ^ SW_MASK;

	printf("%llx\n", *swid_xor_ipad);
}

/**
 * being used to calculate the image hash
 *
 */
#define HW_ID_MASK		0x5c5c5c5cull
#define OEM_ID_MASK		0x00005c5cull
#define OEM_MODEL_ID_MASK	0x00005c5cull

void generate_hwid_opad(char *hw_id, char *oem_id, char *oem_model_id,
					unsigned long long *hwid_xor_opad)
{
	unsigned long long val;

	val = strtoull(hw_id, NULL, 16);
	*hwid_xor_opad = (((val >> 32) ^ HW_ID_MASK) << 32);

	val = strtoul(oem_id, NULL, 16);
	*hwid_xor_opad |= ((val ^ OEM_ID_MASK) << 16);

	val = strtoul(oem_model_id, NULL, 16);
	*hwid_xor_opad |= (val ^ OEM_MODEL_ID_MASK) & 0xffff;

	printf("%llx\n", *hwid_xor_opad);
}

int create_file(char *name, char *buffer, int size)
{
	int fd;

	fd = open(name, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
	if (fd == -1) {
		perror(name);
		return 0;
	}

	if (write(fd, buffer, size) == -1) {
		close(fd);
		return 0;
	}

	close(fd);
	return 1;
}

char *create_xor_ipad_opad(char *f_xor, unsigned long long *xor_buffer)
{
	int fd;
	char *file;
	unsigned long long sw_id, sw_id_be;

	file = mktemp(f_xor);
	fd = open(file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
	if (fd == -1) {
		perror(file);
		return NULL;
	}

	sw_id = *xor_buffer;
	sw_id_be = htobe64(sw_id);
	write(fd, &sw_id_be, sizeof(sw_id_be));
	close(fd);
	return file;
}

char *read_file(char *file_name, size_t *file_size)
{
	int fd;
	struct stat st;
	char *buffer;

	fd = open(file_name, O_RDONLY);
	if (fd == -1) {
		perror(file_name);
		return NULL;
	}

	memset(&st, 0, sizeof(struct stat));
	fstat(fd, &st);
	buffer = malloc(st.st_size * sizeof(buffer));
	if (buffer == NULL) {
		close(fd);
		return NULL;
	}

	if (read(fd, buffer, st.st_size) == -1) {
		close(fd);
		return NULL;
	}

	*file_size = (size_t) st.st_size;
	close(fd);
	return buffer;
}

int generate_hash(char *cert, char *sw_file, char *hw_file)
{
	unsigned long long swid_xor_ipad, hwid_xor_opad;
	char *tmp;
	char *sw_id_str = find_value(cert, "SW_ID", 17);
	char *hw_id_str = find_value(cert, "HW_ID", 17);
	char *oem_id_str = find_value(cert, "OEM_ID", 5);
	char *oem_model_id_str = find_value(cert, "MODEL_ID", 5);
	char f_sw_xor[] = "/tmp/swid_xor_XXXXXX";
	char f_hw_xor[] = "/tmp/hwid_xor_XXXXXX";

	if (sw_id_str == NULL || hw_id_str == NULL || oem_id_str == NULL || oem_model_id_str == NULL) {
		if (sw_id_str != NULL) {
			free(sw_id_str);
		}
		if (hw_id_str != NULL) {
			free(hw_id_str);
		}
		if (oem_id_str != NULL) {
			free(oem_id_str);
		}
		if (oem_model_id_str != NULL) {
			free(oem_model_id_str);
		}
		return 0;
	}
	printf("sw_id=%s\thw_id=%s\t", sw_id_str, hw_id_str);
	printf("oem_id=%s\toem_model_id=%s\n", oem_id_str, oem_model_id_str);

	generate_swid_ipad(sw_id_str, &swid_xor_ipad);
	tmp = create_xor_ipad_opad(f_sw_xor, &swid_xor_ipad);
	if (tmp == NULL) {
		free(sw_id_str);
		free(hw_id_str);
		free(oem_id_str);
		free(oem_model_id_str);
		return 0;
	}
	strlcpy(sw_file, tmp, 32);

	generate_hwid_opad(hw_id_str, oem_id_str, oem_model_id_str, &hwid_xor_opad);
	tmp = create_xor_ipad_opad(f_hw_xor, &hwid_xor_opad);
	if (tmp == NULL) {
		free(sw_id_str);
		free(hw_id_str);
		free(oem_id_str);
		free(oem_model_id_str);
		return 0;
	}
	strlcpy(hw_file, tmp, 32);

	free(sw_id_str);
	free(hw_id_str);
	free(oem_id_str);
	free(oem_model_id_str);

	return 1;
}

void remove_file(char *sw_file, char *hw_file, char *code_file, char *pub_file)
{
	remove(sw_file);
	remove(hw_file);
	remove(code_file);
	remove(pub_file);
	remove("src");
	remove("sig");
	remove("cert");
}

/**
 * is_component_authenticated() usage the code, signature & public key retrieved
 * for each component.
 *
 * @src: char *
 * @sig: char *
 * @cert: char *
 */
int is_component_authenticated(char *src, char *sig, char *cert)
{
	char command[256];
	char *computed_hash;
	char *reference_hash;
	char pub_key[] = "/tmp/pub_keyXXXXXX", *pub_file;
	char code_hash[] = "/tmp/code_hash_XXXXXX", *code_file;
	char tmp_hash[] = "/tmp/tmp_hash_XXXXXX", *tmp_file;
	char f_computed_hash[] = "/tmp/computed_hash_XXXXXX", *computed_file;
	char f_reference_hash[] = "/tmp/reference_hash_XXXXXX", *reference_file;
	char sw_file[32],hw_file[32];
	int retval;
	size_t comp_hash_size, ref_hash_size;

	if (!create_file("src", src, src_size) || !create_file("sig", sig, SIG_SIZE) ||
			!create_file("cert", cert, CERT_SIZE)) {
		return 0;
	}

	pub_file = mktemp(pub_key);
	snprintf(command, sizeof(command),
		"openssl x509 -in cert -pubkey -inform DER -noout > %s", pub_file);
	retval = system(command);
	if (retval != 0) {
		remove("src");
		remove("sig");
		remove("cert");
		printf("Error generating public key\n");
		return 0;
	}

	retval = generate_hash(cert, sw_file, hw_file);
	if (retval == 0) {
		return 0;
	}

	code_file = mktemp(code_hash);
	snprintf(command, sizeof(command),
		"openssl dgst -sha256 -binary -out %s src", code_file);
	retval = system(command);
	if (retval != 0) {
		remove_file(sw_file, hw_file, code_file, pub_file);
		printf("Error in openssl digest\n");
		return 0;
	}

	tmp_file = mktemp(tmp_hash);
	snprintf(command, sizeof(command),
		"cat %s %s | openssl dgst -sha256 -binary -out %s",
						sw_file, code_file, tmp_file);
	retval = system(command);
	if (retval != 0) {
		remove_file(sw_file, hw_file, code_file, pub_file);
		remove(tmp_file);
		printf("Error generating temp has\n");
		return 0;
	}

	computed_file = mktemp(f_computed_hash);
	snprintf(command, sizeof(command),
		"cat %s %s | openssl dgst -sha256 -binary -out %s",
						hw_file, tmp_file, computed_file);
	retval = system(command);
	if (retval != 0) {
		remove_file(sw_file, hw_file, code_file, pub_file);
		remove(tmp_file);
		remove(computed_file);
		printf("Error generating hash\n");
		return 0;
	}

	reference_file = mktemp(f_reference_hash);
	snprintf(command, sizeof(command),
		"openssl rsautl -in sig -pubin -inkey %s -verify > %s",
						pub_file, reference_file);
	retval = system(command);
	if (retval != 0) {
		remove_file(sw_file, hw_file, code_file, pub_file);
		remove(tmp_file);
		remove(computed_file);
		remove(reference_file);
		printf("Error generating reference hash\n");
		return 0;
	}

	computed_hash = read_file(computed_file, &comp_hash_size);
	reference_hash = read_file(reference_file, &ref_hash_size);
	if (computed_hash == NULL || reference_hash == NULL) {
		remove_file(sw_file, hw_file, code_file, pub_file);
		remove(tmp_file);
		remove(computed_file);
		remove(reference_file);
		free(computed_hash?computed_hash:reference_hash);
		return 0;
	}

	remove_file(sw_file, hw_file, code_file, pub_file);
	remove(tmp_file);
	remove(computed_file);
	remove(reference_file);
	if (memcmp(computed_hash, reference_hash, ref_hash_size) ||
			(comp_hash_size != ref_hash_size)) {
		free(computed_hash);
		free(reference_hash);
		printf("Error: Hash or file_size not equal\n");
		return 0;
	}
	free(computed_hash);
	free(reference_hash);
	return 1;
}

/**
 * is_image_authenticated() iterates through each component and check
 * whether individual component is authenticated. If not, abort the FW
 * upgrade process.
 *
 * @img: char *
 */
int is_image_authenticated(void)
{
	int i;
	char *src, *sig, *cert;
	for (i = 0; i < NO_OF_SECTIONS; i++) {
		if (!sections[i].is_present) {
			continue;
		}
		if (!sections[i].split_components(&sections[i], &src, &sig, &cert)) {
			printf("Error while splitting code/signature/Certificate from %s\n",
					sections[i].file);
			return 0;
		}
		if (!is_component_authenticated(src, sig, cert)) {
			printf("Error while authenticating %s\n", sections[i].file);
			return 0;
		}
	}

	return 1;
}

int sec_image_auth(void)
{
	int fd, i, len;
	char *buf = NULL;

	fd = open(SEC_AUTHENTICATE_FILE, O_RDWR);
	if (-1 == fd) {
		perror(SEC_AUTHENTICATE_FILE);
		return 1;
	}
	buf = (char*)malloc(SIG_SIZE);
	if (buf == NULL) {
		perror("Memory allocation failed\n");
		close(fd);
		return 1;
	}
	for (i = 0; i < NO_OF_SECTIONS; i++) {
		if (!sections[i].is_present) {
			continue;
		}

		len = snprintf(buf, SIG_SIZE, "%s %s", sections[i].img_code, sections[i].file);
		if (!strncmp(sections[i].type, "rootfs", strlen("rootfs"))) {
			struct stat sb;
			if (stat(TEMP_METADATA_PATH, &sb) == -1)
				continue;

			len = snprintf(buf, SIG_SIZE, "%s %s %s", sections[i].img_code,
						TEMP_METADATA_PATH, TEMP_SHA_KEY_PATH);
		}

		if (len < 0 || len > SIG_SIZE) {
			perror("Array out of Index\n");
			free(buf);
			close(fd);
			return 1;
		}
		if (write(fd, buf, len) != len) {
			perror("write");
			printf("%s Image authentication failed\n", buf);
			free(buf);
			close(fd);
			return 1;
		}
	}
	close(fd);
	free(buf);
	remove_file(TEMP_KERNEL_PATH,TEMP_ROOTFS_PATH, TEMP_METADATA_PATH, TEMP_SHA_KEY_PATH);
	return 0;
}

int do_board_upgrade_check(char *img)
{
	if (is_tz_authentication_enabled()) {
		/* If image is having signed rootfs image, then extract kernel and rootfs binary for parsing metadata. */
		if (is_rootfs_auth_enabled()) {
			printf("rootfs image authentication is enabled ...\n");
			extract_rootfs_binary(check_image_exist("rootfs-"));
			extract_ubi_volume("ubi_rootfs", check_image_exist("ubi-"), TEMP_ROOTFS_PATH);
		}
		printf("TZ authentication enabled ...\n");
		if (!load_sections()) {
			printf("Error: Failed to load sections from image: %s\n", img);
			return 1;
		}
		return sec_image_auth();
	} else if (is_authentication_check_enabled()) {
		if (!get_sections()) {
			printf("Error: %s is not a signed image\n", img);
			return 1;
		}

		if (!is_image_authenticated()) {
			printf("Error: \"%s\" couldn't be authenticated. Abort...\n", img);
			return 1;
		}

		if (!check_image_version()) {
			printf("Error: \"%s\" couldn't be upgraded. Abort...\n", img);
			return 1;
		}
	}

	return 0;
}
